package cn.yjh.utils;

import java.io.IOException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javassist.*;
import javassist.bytecode.CodeAttribute;
import javassist.bytecode.LocalVariableAttribute;
import javassist.bytecode.MethodInfo;

import cn.yjh.api.impl.ConfigurationServiceImpl;

/**
 * @author yW0041229
 * @since 2022/2/14 16:32
 */
public class JavaSsistUtils {

    public static void main(String[] args) throws IOException {
        Method[] methods = ConfigurationServiceImpl.class.getDeclaredMethods();
        for (Method m : methods) {
            List<String> paramNames = getParamsName(m.getName(), ConfigurationServiceImpl.class);
            List<Class> paramTypes = getParamsType(m.getName(), ConfigurationServiceImpl.class);
            System.out.println(paramNames.toString());
            System.out.println(paramTypes.toString());
        }

    }


    /**
     * 根据方法名获取参数列表每个参数的名称
     *
     * @param methodName
     * @param clazz
     * @return
     */
    public static List<String> getParamsName(String methodName, Class<?> clazz) {
        List<String> paramNames = new ArrayList<>();
        ClassPool pool = ClassPool.getDefault();
        try {
            CtClass ctClass = pool.getCtClass(clazz.getName());
            CtMethod ctMethod = ctClass.getDeclaredMethod(methodName);
            // 使用javassist的反射方法的参数名
            MethodInfo methodInfo = ctMethod.getMethodInfo();
            CodeAttribute codeAttribute = methodInfo.getCodeAttribute();
            LocalVariableAttribute attr = (LocalVariableAttribute) codeAttribute.getAttribute(LocalVariableAttribute.tag);
            if (attr != null) {
                CtClass[] paramTypes = ctMethod.getParameterTypes();
                int len = paramTypes.length;
                // 非静态的成员函数的第一个参数是this
                int pos = Modifier.isStatic(ctMethod.getModifiers()) ? 0 : 1;
                for (int i = 0; i < len; i++) {
                    String paramName = attr.variableName(i + pos);
                    // 如果方法中使用了 try-catch 那么第一个参数是 e 所以跳过
                    if ( "this".equals(paramName) || "e".equals(paramName)) {
                        len++;
                    } else {
                        paramNames.add(paramName);
                    }
                }
            }
            return paramNames;
        } catch (NotFoundException e) {
            e.printStackTrace();
            return null;
        }
    }

    public static List<Class> getParamsType(String methodName, Class<?> clazz) {
        List<Class> paramTypes = new ArrayList<>();
        ClassPool pool = ClassPool.getDefault();
        try {
            CtClass ctClass = pool.getCtClass(clazz.getName());
            CtMethod ctMethod = ctClass.getDeclaredMethod(methodName);
            // 使用javassist的反射方法的参数名
            MethodInfo methodInfo = ctMethod.getMethodInfo();
            CodeAttribute codeAttribute = methodInfo.getCodeAttribute();
            LocalVariableAttribute attr = (LocalVariableAttribute) codeAttribute.getAttribute(LocalVariableAttribute.tag);
            if (attr != null) {
                CtClass[] types = ctMethod.getParameterTypes();
                for (CtClass type:types){
                    String name = type.getName();
                    Class<?> typeClazz = null;
                    try {
                        typeClazz = Class.forName(name);
                    } catch (ClassNotFoundException e) {
                        e.printStackTrace();
                    }
                    paramTypes.add(typeClazz);
                }
            }
            return paramTypes;
        } catch (NotFoundException e) {
            e.printStackTrace();
            return null;
        }
    }

}
